/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.debugger.jpda;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Modifier;
import java.util.ResourceBundle;
import java.util.List;
import javax.swing.SwingUtilities;
import com.sun.jdi.Value;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.Field;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.StackFrame;
import com.sun.jdi.InvalidTypeException;
import com.sun.tools.example.debug.expr.ExpressionParser;
import com.sun.tools.example.debug.expr.ParseException;
import org.openide.TopManager;
import org.openide.util.NbBundle;
import org.openide.debugger.Watch;
import org.openide.debugger.DebuggerNotFoundException;
import org.netbeans.modules.debugger.support.VariableImpl;
import org.netbeans.modules.debugger.support.AbstractVariable;
import org.netbeans.modules.debugger.support.util.Validator;
import org.netbeans.modules.debugger.support.util.Protector;
import org.netbeans.modules.debugger.support.util.Utils;
/**
* JPDA implemetation of variable.
*
* @author Jan Jancura
*/
public class JPDAVariable extends VariableImpl {
/** generated Serialized Version UID */
static final long serialVersionUID = -4908841115435123749L;
/** bundle to obtain text information from */
private static ResourceBundle bundle = NbBundle.getBundle (JPDAVariable.class);
//static int num = 0;
//int myNum;
// variables .........................................................................
/** Variables parentObject & (name | index) identifies variable. */
private transient ObjectReference parentObject;
/** Current value - used for getFields (). */
protected transient Value remoteValue;
/** Current field. */
protected transient Field remoteField;
/** Stack Frame for locales. */
private transient StackFrame stackFrame = null;
/** Cashing of children variables */
private transient ObjectReference oldObject;
/** Cashing of children variables */
private transient AbstractVariable[] oldFields;
protected transient JPDADebugger debugger;
// init ...............................................................................
protected void finalize () {
//S ystem.out.println("fuck out JPDAVariable " + getVariableName ());
}
/**
* Non public constructor.
*/
JPDAVariable (JPDADebugger debugger, boolean validate) {
super (debugger, validate ? debugger.getValidator () : null);
this.debugger = debugger;
}
/**
* Creates variable.
*/
private JPDAVariable (
JPDADebugger debugger,
ObjectReference parentObject,
Field field
) {
this (debugger, true);
update (field, parentObject);
//S ystem.out.println("new JPDAVariable " + getVariableName ());
}
/**
* Creates local variable. Called from thread, LineBreakpoint ...
*/
JPDAVariable (
JPDADebugger debugger,
String name,
Value value,
String type
) {
this (debugger, true);
update (name, value, type);
//S ystem.out.println("new JPDAVariable " + getVariableName ());
}
/**
* Creates local variable. Called from thread.
*/
JPDAVariable (
JPDADebugger debugger,
String name,
Value value,
String type,
StackFrame stackFrame
) {
this (debugger, true);
update (name, value, type, stackFrame);
//S ystem.out.println("new JPDAVariable " + getVariableName ());
}
/**
* Creates member varaible of array.
*/
private JPDAVariable (
JPDADebugger debugger,
ArrayReference array,
String parentName,
int index,
String type
) {
this (debugger, true);
update (parentName, array, index, type);
//S ystem.out.println("new JPDAVariable " + getVariableName ());
}
private void readObject (java.io.ObjectInputStream in)
throws java.io.IOException, ClassNotFoundException {
in.defaultReadObject ();
try {
debugger = (JPDADebugger) TopManager.getDefault ().getDebugger ();
} catch (DebuggerNotFoundException e) {
throw new java.io.IOException ();
}
}
// AbstractVariable implementation ...........................................
/**
* Setter that allows to change value of the watched variable.
*
* @param value text representation of the value
* @exception DebuggerException if the value cannot be changed or the
* string does not represent valid value
*/
public void setAsText (final String value) {
if (debugger.getState () != debugger.DEBUGGER_STOPPED) return;
if (isCloned) return; // fixed watch
final JPDAThread tt = (JPDAThread) debugger.getCurrentThread ();
if (tt == null) return;
String errorMsg = getErrorMessage ();
if (errorMsg != null) {
throw Utils.localizeException (
new IllegalArgumentException (),
bundle.getString ("EXC_Value_cannt_be_set_info") + " " + errorMsg // NOI18N
);
}
ThreadReference rt = tt.getThreadReference ();
final StackFrame sf;
try {
sf = rt.frame (0);
} catch (Exception e) {
return;
}
Value v = null;
try {
v = (Value) new Protector ("JPDAVariable.ExpressionParser") { // NOI18N
public Object protect () throws Exception {
Value vv = ExpressionParser.evaluate (
value,
debugger.virtualMachine,
new ExpressionParser.GetFrame () {
public StackFrame get () {
return sf;
}
}
);
return vv;
}
}.throwAndWait (null);
} catch (ParseException e) {
// a pacth for static variables ******************************************
try {
ReferenceType refType = sf.thisObject ().referenceType ();
v = refType.getValue (refType.fieldByName (value));
}
catch (Exception ee) {
throw Utils.localizeException (
new IllegalArgumentException (),
bundle.getString ("EXC_Value_cannt_be_set_info") + " " + e.getMessage() //NOI18N
);
}
// end of patch ***********************************************************
}
catch (Throwable e) {
throw Utils.localizeException (
new IllegalArgumentException (),
bundle.getString ("EXC_Value_cannt_be_set_info") + " " + e.getMessage() //NOI18N
);
}
// find the variable
Boolean result = new Boolean (false);
final Value v2 = v;
try {
final int ind = index;
result = (Boolean) new Protector ("JPDAVariable.ExpressionParser") { // NOI18N
public Object protect () throws Exception {
if (stackFrame != null) { // locales
LocalVariable localVariable = stackFrame.visibleVariableByName (
getVariableName ()
);
if (localVariable != null)
stackFrame.setValue (localVariable, v2);
else // local variable variable cannot be found
return new Boolean (false);
}
else {
if (parentObject == null) { // watch
LocalVariable localVariable = sf.visibleVariableByName (
getVariableName ()
);
if (localVariable != null)
sf.setValue (localVariable, v2);
else {
ObjectReference obj = sf.thisObject ();
if (obj == null)
return new Boolean (false);
ReferenceType ref = obj.referenceType ();
Field field = ref.fieldByName (getVariableName ());
if (field != null)
sf.thisObject ().setValue (field, v2);
else // variable is an expression
return new Boolean (false);
}
}
else { // parentObject != null
if (parentObject instanceof ArrayReference)
((ArrayReference) parentObject).setValue (ind, v2);
else
parentObject.setValue (remoteField, v2);
}
}
return new Boolean (true);
}
}.throwAndWait (null);
}
catch (InvalidTypeException e) {
throw Utils.localizeException (
new IllegalArgumentException (),
bundle.getString ("EXC_Wrong_type")
);
}
catch (Exception e) {
throw Utils.localizeException (
new IllegalArgumentException (),
bundle.getString ("EXC_Value_cannt_be_set")
);
}
if (! result.booleanValue ()) {
// variable is an expression
throw Utils.localizeException (
new IllegalArgumentException (),
bundle.getString ("EXC_Cannot_assign_to_expression")
);
}
if (v == null) {
// setNull
remoteValue = null;
type = ""; // NOI18N
update ();
// end of setNull
}
else {
// update
remoteValue = v;
type = v.type ().name ();
update ();
// end of update
}
if (pcs != null)
pcs.firePropertyChange (null, null, null);
}
/**
* If this AbstractVariable object represents instance of some class or array this method
* returns variables (static and non-static) of this object.
*
* @return variables (static and non-static) of this object.
*/
public AbstractVariable[] getFields () {
final String name = this.name;
//S ystem.out.println ("getFields " + remoteValue + " " + oldObject); // NOI18N
if ((remoteValue == null) ||
!(remoteValue instanceof ObjectReference)
) {
//S ystem.out.println ("getFields prim."); // NOI18N
return new JPDAVariable [0];
}
if ((oldObject != null) && oldObject.equals (remoteValue)) {
// the same set of children => validate only
//S ystem.out.println ("getFields return old " + oldFields + " " + oldFields.length); // NOI18N
// int i, k = oldFields.length;
// for (i = 0; i < k; i++) oldFields [i].validate ();
return oldFields;
}
try {
AbstractVariable[] variable;
ObjectReference remoteObject = (ObjectReference)remoteValue;
if (remoteValue instanceof ArrayReference) {
final String type = innerType.substring (0, innerType.length () - 2);
ArrayReference array = (ArrayReference) remoteValue;
int i, k = array.getValues ().size ();
variable = new AbstractVariable [k];
for (i = 0; i < k; i++)
variable [i] = new JPDAVariable (
debugger,
array,
name,
i,
type
);
//S ystem.out.println ("getFields return array " + variable + " " + variable.length); // NOI18N
} else {
List fields = ((ObjectReference) remoteValue).referenceType ().allFields ();
int i, k = fields.size ();
variable = new AbstractVariable [k];
for (i = 0; i < k; i++)
variable [i] = new JPDAVariable (
debugger,
remoteObject,
(Field) fields.get (i)
);
//S ystem.out.println ("getFields return fields " + variable + " " + variable.length); // NOI18N
}
oldObject = remoteObject;
oldFields = variable;
return variable;
} catch (Exception e) {
//e.printStackTrace ();
return new JPDAVariable [0];
}
}
/**
* Returns true if this variable hasn't any fields.
*
* @return True if this variable hasn't any fields.
*/
public boolean isLeaf () {
return (remoteValue == null) || !(remoteValue instanceof ObjectReference);
}
// other methods ....................................................................................
/**
* I am member of an array.
* Update modifiers, type, remoteValue, parentObject.
*/
boolean update (
String name,
ArrayReference array,
int index,
String type
) {
this.name = name + " [" + index + "]"; // NOI18N
parentObject = array;
this.index = index;
this.type = type;
modifiers = ""; // NOI18N
try {
remoteValue = array.getValue (index);
} catch (Exception e) {
return false;
}
update ();
return true;
}
/**
* Init for remoteObject
* modifiers, type, remoteValue, parentObject
*/
boolean update (
Field field,
ObjectReference parentObject
) {
this.name = field.name ();
this.parentObject = parentObject;
try {
remoteField = field;
if (remoteField == null) return false;
remoteValue = parentObject.getValue (remoteField);
modifiers = Modifier.toString (remoteField.modifiers ());
type = remoteField.typeName ();
} catch (Exception e) {
return false;
}
update ();
return true;
}
/**
* For local variables. (locales or watch)
*/
void update (
String name,
Value remoteValue,
String type
) {
this.name = name;
this.remoteValue = remoteValue;
this.parentObject = null;
this.type = type;
modifiers = ""; // NOI18N
update ();
}
/**
* For local variables, called from thread.
*/
void update (
String name,
Value remoteValue,
String type,
StackFrame stackFrame
) {
this.stackFrame = stackFrame;
update (name, remoteValue, type);
}
void setNull () {
remoteValue = null;
parentObject = null;
modifiers = ""; // NOI18N
type = ""; // NOI18N
update ();
}
protected void setError (String description) {
remoteValue = null;
parentObject = null;
super.setError (description);
}
/**
*
*/
public void validate () {
if (isCloned) {
if (isObject) {
value = remoteValue.toString ();
}
} else {
if (index != -1)
if (parentObject instanceof ArrayReference)
try {
remoteValue = ((ArrayReference) parentObject).getValue (index);
} catch (Exception e) {
}
else remoteValue = null;
else
try {
remoteValue = parentObject.getValue (remoteField);
} catch (Exception e) {
}
update ();
}
if (pcs != null) pcs.firePropertyChange (null, null, null);
}
/**
* @return true if debugger is stopped.
*/
public boolean canValidate () {
return debugger.getState () == JPDADebugger.DEBUGGER_STOPPED;
}
/**
* @return true, variable can be removed from validator when debugger is finished
*/
public boolean canRemove () {
return true;
}
/**
* remoteValue => isObject, isArray, value, innerType
* + fire
*/
private void update () {
if (remoteValue == null) {
isObject = false;
isArray = false;
value = null;
innerType = ""; // NOI18N
} else
try {
isObject = remoteValue instanceof ObjectReference;
isArray = remoteValue instanceof ArrayReference;
if (isArray) {
value = remoteValue.toString ();
innerType = ((ObjectReference)remoteValue).referenceType ().name ();
} else
if (isObject) {
value = remoteValue.toString ();
innerType = ((ObjectReference)remoteValue).referenceType ().name ();
} else {
innerType = remoteValue.type ().name ();
value = remoteValue.toString ();
}
} catch (Exception e) {
// exception will be notified
type = null;
value = e.toString ();
}
}
void setValue (String v) {
value = v;
}
void firePropertyChange () {
super.firePropertyChange (null, null, null);
}
// Helper methods enabling delegating in JPDAWatch
JPDADebugger getDebugger () {
return debugger;
}
void setErrorMessage (String errMessage) {
errorMessage = errMessage;
}
Value getRemoteValue () {
return remoteValue;
}
void setRemoteValue (Value val) {
remoteValue = val;
}
void setError_protected (String description) {
setError (description);
}
java.lang.Object clone_protected () {
return clone ();
}
}
/*
* Log
* 10 Gandalf-post-FCS1.8.4.0 3/28/00 Daniel Prusa
* 9 Gandalf 1.8 1/14/00 Daniel Prusa NOI18N
* 8 Gandalf 1.7 1/13/00 Daniel Prusa NOI18N
* 7 Gandalf 1.6 1/3/00 Daniel Prusa
* 6 Gandalf 1.5 12/30/99 Daniel Prusa Validator placed into
* Watch
* 5 Gandalf 1.4 12/21/99 Daniel Prusa Interfaces Debugger,
* Watch, Breakpoint changed to abstract classes.
* 4 Gandalf 1.3 11/8/99 Jan Jancura Somma classes renamed
* 3 Gandalf 1.2 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 2 Gandalf 1.1 10/5/99 Jan Jancura Serialization of
* debugger.
* 1 Gandalf 1.0 9/2/99 Jan Jancura
* $
*/